package cn.wangkai.peanut.util.mvc;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import cn.wangkai.peanut.util.RequestContext;

public final class ResourceServlet extends HttpServlet {
	/**
	 * 资源文件过滤器
	 */
	private static final long serialVersionUID = 5372615627861275900L;
	private static Log log = LogFactory.getLog(ResourceServlet.class);
	private static String etag;
	private String encoding = null;
	private boolean isdebug = false;
	private static Map<String, String> mimeTypes;
	private static Map<String, byte[]> rescache;
	private static long servletContainerStartTime;

	static {
		mimeTypes = new HashMap<String, String>();
		rescache = new HashMap<String, byte[]>();
		long now = System.currentTimeMillis();
		servletContainerStartTime = now - now % 1000L;
		etag = "\"" + servletContainerStartTime + '"';
	}

	/**
	 * 初始化资源
	 */
	public void init() throws ServletException {
		log.debug("初始化资源文件过滤器......");
		encoding = getInitParameter("encoding");
		isdebug = BooleanUtils.toBoolean(getInitParameter("isdebug"));
		if (encoding == null) encoding = "utf-8";
		readmimetype();
	}

	/**
	 * 读取需要过滤文件类型
	 */
	public void readmimetype(){
		URL resource = ResourceServlet.class.getResource("/xwy-resource.xml");
		InputStream is = null;
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder db;
		try {
			db = factory.newDocumentBuilder();
	        is = resource.openStream();
	        Document doc = db.parse(is);
	        Element elmtInfo = doc.getDocumentElement();
	        NodeList nodes = elmtInfo.getChildNodes();
	        for (int i = 0; i < nodes.getLength(); i++){
	            Node result = nodes.item(i);
	            if (result.getNodeType() == Node.ELEMENT_NODE && "mime-mapping".equalsIgnoreCase(result.getNodeName())){
	                NodeList ns = result.getChildNodes();
	            	String extension =null;
	            	String mimetype =null;
	                for (int j = 0; j < ns.getLength(); j++){
	                    Node record = ns.item(j);
	                    if (record.getNodeType() == Node.ELEMENT_NODE && "extension".equalsIgnoreCase(record.getNodeName())){
	                    	extension = record.getTextContent();
	                    }
	                    if (record.getNodeType() == Node.ELEMENT_NODE && "mime-type".equalsIgnoreCase(record.getNodeName())){
	                    	mimetype = record.getTextContent();
	                    }
	                }
	                log.debug(extension+":"+mimetype);
	                mimeTypes.put(extension,mimetype);
	            }
	        }
		} catch (ParserConfigurationException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		} catch (SAXException e) {
			log.error(e);
		}finally{
			try {
				if(is!=null)
					is.close();
			} catch (IOException e) {}
		}

	}
	
	public void destroy() {
		super.destroy();
	}

	protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		//如果被缓冲，则直接返回304
		if(checkcache(request)){
			response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
			return;
		}
		//文件路径
		String theServletPath = request.getServletPath() + request.getPathInfo();
		String theExtension = StringUtils.right(theServletPath,theServletPath.length() - theServletPath.lastIndexOf(".") - 1);
		if(theExtension!=null) theExtension =theExtension.trim().toLowerCase();
		//获取扩展名
		String mimeType = mimeTypes.get(theExtension);
		String resstr = "";
		//类型符合
		if(mimeType!=null){
			byte bytes[] = null;
			if(!isdebug){//不是开发模式下先读取缓冲数据
				if(rescache.containsKey(theServletPath)){
					bytes = (byte[]) rescache.get(theServletPath);
					if(bytes==null){
						response.sendError(HttpServletResponse.SC_NOT_FOUND);
						return;
					}
				}
			}
			//
			if(bytes==null){
				//查找文件
				File file = new File(RequestContext.root()+"/"+theServletPath);
				if(file.isFile()){
					resstr = file.getPath();
					bytes = getResourceByFile(theExtension, file);
				}else{
					URL resource = ResourceServlet.class.getResource(theServletPath);
					if (resource != null){
						resstr = resource.toString();
						URLConnection urlConnection = resource.openConnection();
						if (!isdebug) {
							bytes = getResourceBytesWithBuffer(theExtension,urlConnection);
						}else{
							bytes = getResourceBytes(theExtension, urlConnection);
						}
					}
				}
			}
			if(!isdebug){//如果不是开发模式记录
				if(bytes!=null){
					response.setDateHeader("Last-Modified", servletContainerStartTime);
					response.setHeader("ETag", etag);
				}
				synchronized (rescache) {
					rescache.put(theServletPath, bytes);
				}
			}
			
			if(bytes!=null){
				log.debug("theServletPath="+theServletPath +"\tresource="+ resstr + "\t mimeType=" +mimeType);
				if(StringUtils.isBlank(mimeType)){
					mimeType="*";
				}
				log.debug("response.addHeader(\"Content-Type\", "+mimeType+"+\";charset=utf-8\");");
				response.addHeader("Content-Type", mimeType+";charset=utf-8");
				response.setContentLength(bytes.length);
				try {
					//输出文件类型
//					01.////css mine特殊处理 悟空的代码 www.7es.cn 
//					02.if (fileext == ".css") //首先判断输出的是css文件 
//					03.{ 
//					04.    Response.AddHeader("Content-Type", "text/css;charset=utf-8"); //在response返回信息中加入css mime头 
//					05.}

					IOUtils.write(bytes, response.getOutputStream());
					return;
				} catch (IOException e) {
					log.error(e);
				}
			}
		}
		response.sendError(HttpServletResponse.SC_NOT_FOUND);
		return;
	}

	/**
	 * 读取文件中的数据
	 * @param theExtension
	 * @param file
	 * @return
	 * @throws IOException
	 */
	private byte[] getResourceByFile(String theExtension,File file) throws IOException {
		byte[] bytes = null;
		InputStream is = null;
		if(file!=null){
			try{
				is = new FileInputStream(file);
				int length = (int) file.length();
				if(length>Integer.MAX_VALUE){   //当文件的长度超过了int的最大值
					throw new IOException("this file is max ");
				}
				bytes = new byte[length];
				int offset = 0;
				int numRead = 0;
				while(offset<bytes.length&&(numRead=is.read(bytes,offset,bytes.length-offset))>=0){
					offset+=numRead;
				}
				//如果得到的字节长度和file实际的长度不一致就可能出错了
				if(offset<bytes.length){
					throw new IOException("file length is error");
				}
			}finally{
				if(is!=null) is.close();
			}
		}
		return bytes;
	}
	
	/**
	 * 读取url中的数据至OutputStream
	 * @param theExtension
	 * @param url
	 * @return
	 */
	private byte[] getResourceBytesWithBuffer(String theExtension,URLConnection url) {
		java.io.InputStream inputStream = null;
		byte bt[];
		try {
			inputStream = url.getInputStream();
			ByteArrayOutputStream outStm = new ByteArrayOutputStream();
			IOUtils.copy(new BufferedInputStream(inputStream), outStm);
			outStm.flush();
			byte bytes[] = outStm.toByteArray();
			bt = bytes;
			return bt;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e);
		} finally {
			IOUtils.closeQuietly(inputStream);
		}
	}

	/**
	 * 直接读取url数据至Byte
	 * @param theExtension
	 * @param url
	 * @return
	 */
	private byte[] getResourceBytes(String theExtension,URLConnection url) {
		InputStream is = null;
		byte bt[];
		try {
			is = url.getInputStream();
			bt = IOUtils.toByteArray(is);
			return bt;
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e);
		} finally {
			IOUtils.closeQuietly(is);
		}
	}

	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
	
	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
	
	@Override
	protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
	
	@Override
	protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
	
	@Override
	protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}
	
	@Override
	protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doGet(req, resp);
	}

	/**
	 * 判断文件是否需要重新读取
	 * @param req
	 * @return
	 */
	private boolean checkcache(HttpServletRequest req){
		if(isdebug){//开发模式不判断
			return false;
		}
		long modifiedSince = req.getDateHeader("If-Modified-Since");//取得客户端传上来的时间戳 if-modified
		if(modifiedSince != -1L){
			modifiedSince -= modifiedSince % 1000L;
		}
		String givenEtag = req.getHeader("If-None-Match");
		if(givenEtag == null){
			return modifiedSince > servletContainerStartTime;
		}
		if(modifiedSince == -1L){
			return !etag.equals(givenEtag);
		}
		return etag.equals(givenEtag) && modifiedSince <= servletContainerStartTime;
	}

	public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {

	}
	
}